package com.junxonline.plat.service.menu.impl;

import com.junxonline.plat.dao.model.Menu;
import com.junxonline.plat.dao.model.Role;
import com.junxonline.plat.dao.model.User;
import com.junxonline.common.dao.pojo.JsTree;
import com.junxonline.common.dao.pojo.JsTreeState;
import com.junxonline.plat.dao.repository.MenuRepository;
import com.junxonline.plat.dao.repository.RoleRepository;
import com.junxonline.plat.dao.vo.MenuVO;
import com.junxonline.plat.dao.vo.PageVO;
import com.junxonline.plat.dao.vo.QueryMenuVO;
import com.junxonline.common.exception.CommonBizException;
import com.junxonline.common.util.CommonUtils;
import com.junxonline.plat.service.base.impl.BaseServiceImpl;
import com.junxonline.plat.service.menu.MenuService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigInteger;
import java.util.*;

@Service
@Transactional
public class MenuServiceImpl extends BaseServiceImpl implements MenuService {

    private Logger logger = LogManager.getLogger();

    @Autowired
    private MenuRepository menuRepository;

    @Autowired
    private RoleRepository roleRepository;

    /**
     * 获取导航菜单数据
     *
     * @param userId
     * @return
     */
    @Override
    public List<Menu> getNavMenu(Long userId) {

        User user = getUser(userId);

        List<Role> roles = user.getRoles();
        List<Menu> menus = new ArrayList<>();

        for (Role role : roles) {
            menus.addAll(menuRepository.findMenusByRoleIdOrderByMenuSortAsc(role.getRoleId()));
        }

        // 去重
        Set<Menu> set = new HashSet<>();
        List<Menu> returnMenus = new ArrayList<>();
        Iterator<Menu> iterator = menus.iterator();
        while (iterator.hasNext()) {
            Menu menu = iterator.next();
            if (set.add(menu)) {
                returnMenus.add(menu);
            }
        }

        // 删除所有的按钮和数据接口
        iterator = returnMenus.iterator();
        while (iterator.hasNext()) {
            Menu menu = iterator.next();
            if (menu.getMenuType().equals(2) || menu.getMenuType().equals(3)) {
                iterator.remove();
            }
        }

        return returnMenus;
    }

    /**
     * 获取按钮数据
     *
     * @param menuUrl
     * @param userId
     * @return
     */
    @Override
    public List<Menu> getButton(String menuUrl, Long userId) {

        User user = getUser(userId);

        List<Role> roles = user.getRoles();
        List<Menu> menus = new ArrayList<>();
        List<Menu> returnButtons = new ArrayList<>();

        Menu parentMenu = menuRepository.findByMenuUrl(menuUrl);

        if (CommonUtils.isNotEmpty(parentMenu)) {

            for (Role role : roles) {
                menus.addAll(menuRepository.findMenusByRoleIdOrderByMenuSortAsc(role.getRoleId()));
            }

            // 去重
            Set<Menu> set = new HashSet<Menu>();
            Iterator<Menu> iterator = menus.iterator();
            while (iterator.hasNext()) {
                Menu menu = iterator.next();
                if (set.add(menu)) {
                    returnButtons.add(menu);
                }
            }

            iterator = returnButtons.iterator();
            while (iterator.hasNext()) {
                Menu menu = iterator.next();
                if (menu.getMenuType().intValue() != 2 || menu.getMenuParentId() == null ||
                        !menu.getMenuParentId().equals(parentMenu.getMenuId().toString())) {
                    iterator.remove();
                }
            }

        }

        return returnButtons;
    }

    /**
     * 获取菜单树数据
     *
     * @return
     */
    @Override
    public List<JsTree> getMenuTree() {

        List<JsTree> jsTrees = new ArrayList<>();

        // 获取所有的菜单数据
        List<Menu> menus = menuRepository.findAll(new Sort(Sort.Direction.ASC, "menuSort"));

        // 构建菜单树
        for (Menu menu : menus) {
            JsTree jsTree = new JsTree();
            jsTree.setId(menu.getMenuId().toString());
            jsTree.setText(menu.getMenuName());

            // 为顶级菜单设置父级Id为# （JsTree数据规范）
            if (CommonUtils.isEmpty(menu.getMenuParentId())) {
                jsTree.setParent("#");
            } else {
                jsTree.setParent(menu.getMenuParentId());
            }


            // 为按钮设置图标
            if (menu.getMenuType().equals(2)) {
                jsTree.setIcon("fa fa-file icon-state-warning");
            }
            // 为数据接口设置图标
            else if (menu.getMenuType().equals(3)) {
                jsTree.setIcon("fa fa-leaf icon-state-success");
            }

            // 为顶级菜单设置默认展开
            if (CommonUtils.isEmpty(menu.getMenuParentId())) {
                jsTree.setState(new JsTreeState(false, false, false));
            }
            // 为非顶级菜单组设置默认不展开
            else {
                jsTree.setState(new JsTreeState(false, false, false));
            }


            jsTrees.add(jsTree);
        }

        return jsTrees;
    }

    /**
     * 获取父级菜单树数据
     *
     * @return
     */
    @Override
    public List<JsTree> getParentMenuTree() {
        List<JsTree> jsTrees = new ArrayList<>();

        // 获取所有的菜单数据
        List<Menu> menus = menuRepository.findAllMenusByMenuTypeIn();

        // 构建菜单树
        for (Menu menu : menus) {
            JsTree jsTree = new JsTree();
            jsTree.setId(menu.getMenuId().toString());
            jsTree.setText(menu.getMenuName());

            // 为顶级菜单设置父级Id为# （JsTree数据规范）
            if (CommonUtils.isEmpty(menu.getMenuParentId())) {
                jsTree.setParent("#");
            } else {
                jsTree.setParent(menu.getMenuParentId());
            }

            jsTree.setState(new JsTreeState(true, false, false));
            jsTrees.add(jsTree);
        }

        return jsTrees;
    }

    /**
     * 获取菜单数据
     *
     * @param vo
     * @return
     */
    @Override
    public Menu getMenu(QueryMenuVO vo) {

        Menu menu = menuRepository.findByMenuId(vo.getMenuId());

        return menu;
    }

    /**
     * 筛选查询菜单
     *
     * @param pageVO
     * @param vo
     * @return
     */
    @Override
    public PageVO queryMenu(PageVO pageVO, QueryMenuVO vo) {
        PageRequest pageRequest = PageRequest.of(pageVO.getPage() - 1, pageVO.getLimit(), new Sort(Sort.Direction.ASC, "menuId"));

        Page<Menu> page = menuRepository.findAll(new Specification<Menu>() {
            @Override
            public Predicate toPredicate(Root<Menu> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

                List<Predicate> params = new ArrayList<>();
                String menuName = vo.getMenuName();
                Integer menuType = vo.getMenuType();
                Integer menuStatus = vo.getMenuStatus();

                if (CommonUtils.isNotEmpty(menuName)) {
                    params.add(criteriaBuilder.like(root.get("menuName"), "%" + menuName + "%"));
                }

                if (CommonUtils.isNotEmpty(menuType)) {
                    params.add(criteriaBuilder.equal(root.get("menuType"), menuType));
                }

                if (CommonUtils.isNotEmpty(menuStatus)) {
                    params.add(criteriaBuilder.equal(root.get("menuStatus"), menuStatus));
                }

                return criteriaBuilder.and(params.toArray(new Predicate[params.size()]));
            }
        }, pageRequest);

        List<Menu> menus = page.getContent();

        for (Menu menu : menus) {

            // 替换菜单类型
            switch (menu.getMenuType()) {
                case 0:
                    menu.setMenuTypeString("菜单组");
                    break;
                case 1:
                    menu.setMenuTypeString("菜单");
                    break;
                case 2:
                    menu.setMenuTypeString("按钮");
                    break;
                case 3:
                    menu.setMenuTypeString("数据");
                    break;
                default:
                    break;
            }

            // 替换菜单状态
            switch (menu.getMenuStatus()) {
                case 0:
                    menu.setMenuStatusString("<span class=\"layui-badge layui-bg-green\">启用</span>");
                    break;
                case 1:
                    menu.setMenuStatusString("<span class=\"layui-badge\">禁用</span>");
                    break;
                default:
                    break;
            }

        }

        pageVO.setTotal(page.getTotalElements());
        pageVO.setData(menus);

        return pageVO;
    }


    /**
     * 添加菜单
     *
     * @param vo
     * @return
     */
    @Override
    public boolean addMenu(MenuVO vo) {

        Menu menu = new Menu();
        menu.setMenuName(vo.getMenuName());
        menu.setMenuType(vo.getMenuType());

        String parentId = vo.getMenuParentId();
        menu.setMenuParentId(parentId);

        if (CommonUtils.isNotEmpty(parentId)) {
            // 获取菜单名称
            Menu tempMenu = menuRepository.findByMenuId(Long.parseLong(parentId));

            if (CommonUtils.isEmpty(tempMenu)) {
                throw CommonBizException.SYSTEM_ERROR;
            }

            menu.setMenuParentName(tempMenu.getMenuName());
        }

        menu.setMenuUrl(vo.getMenuUrl());
        menu.setButtonDataType(vo.getButtonDataType());
        menu.setButtonStyle(vo.getButtonStyle());
        menu.setMenuIcon(vo.getMenuIcon());
        menu.setMenuPermission(vo.getMenuPermission());
        menu.setMenuSort(vo.getMenuSort());
        menu.setMenuStatus(vo.getMenuStatus());

        menuRepository.save(menu);

        // 将此菜单的权限默认添加给管理员
        Role role = roleRepository.findByRoleKey("admin");
        if (CommonUtils.isNotEmpty(role)) {
            role.getMenus().add(menu);
            roleRepository.save(role);
        }

        return true;
    }

    /**
     * 修改菜单
     *
     * @param vo
     * @return
     */
    @Override
    public boolean editMenu(MenuVO vo) {

        Menu menu = menuRepository.findByMenuId(vo.getMenuId());

        if (CommonUtils.isEmpty(menu)) {
            throw CommonBizException.SYSTEM_ERROR;
        }

        menu.setMenuName(vo.getMenuName());
        menu.setMenuType(vo.getMenuType());

        String parentId = vo.getMenuParentId();
        menu.setMenuParentId(parentId);

        if (CommonUtils.isNotEmpty(parentId)) {
            // 获取菜单名称
            Menu tempMenu = menuRepository.findByMenuId(Long.parseLong(parentId));

            if (CommonUtils.isEmpty(tempMenu)) {
                throw CommonBizException.SYSTEM_ERROR;
            }

            menu.setMenuParentName(tempMenu.getMenuName());
        }

        menu.setMenuUrl(vo.getMenuUrl());
        menu.setButtonDataType(vo.getButtonDataType());
        menu.setButtonStyle(vo.getButtonStyle());
        menu.setMenuIcon(vo.getMenuIcon());
        menu.setMenuPermission(vo.getMenuPermission());
        menu.setMenuSort(vo.getMenuSort());
        menu.setMenuStatus(vo.getMenuStatus());

        menuRepository.save(menu);

        return true;
    }

    /**
     * 删除菜单
     *
     * @param menuIds
     * @return
     */
    @Override
    public boolean delMenu(Long[] menuIds) {

        List<Menu> menus = menuRepository.findByMenuIdIn(menuIds);

        for (Menu menu : menus) {

            List<Map<String, BigInteger>> roleIds = menuRepository.findRoleMenusByMenuId(menu.getMenuId());

            // 删除与该菜单相关联的所有中间表数据
            for (Map<String, BigInteger> roleMenu : roleIds) {
                Long menuId = roleMenu.get("menu_id").longValue();
                Long roleId = roleMenu.get("role_id").longValue();
                menuRepository.deleteRoleMenuIn(menuId, roleId);
            }

            // 再删除菜单
            menuRepository.delete(menu);
        }

        return true;
    }

}
